# 导入PyTorch库，核心功能是构建神经网络、自动求导和参数优化
import torch
# 导入numpy库，用于生成连续的x轴数据（如linspace生成0到10的200个点）
import numpy as np
# 导入matplotlib.pyplot库，用于绘制逻辑回归的概率曲线和参考线
import matplotlib.pyplot as plt

# 定义逻辑回归的输入特征x_data：3个样本，每个样本1个特征（如“学习时长”）
# 形状为(3,1)，对应3行1列，符合Linear(1,1)层对“单特征输入”的要求
x_data = torch.tensor([[1.0], [2.0], [3.0]])
# 定义目标标签y_data：3个样本的分类结果（0表示“不及格”，1表示“及格”）
# 形状与x_data一致为(3,1)，且为float类型（BCELoss要求目标值为浮点型）
y_data = torch.tensor([[0.0], [0.0], [1.0]])

# 定义逻辑回归模型类，继承自PyTorch的Module（所有神经网络模型的基类）
class LogisticRegressionModel(torch.nn.Module):
    # 构造函数：初始化模型的层结构和参数
    def __init__(self):
        # 调用父类Module的构造函数，确保模型继承父类的所有功能（如参数管理）
        super(LogisticRegressionModel, self).__init__()
        # 定义线性层：输入特征数=1（每个样本1个特征），输出特征数=1（线性预测值）
        # 该层会自动初始化权重w（形状(1,1)）和偏置b（标量）
        self.linear = torch.nn.Linear(1,1)

    # 前向传播函数：定义模型的计算流程（输入x → 输出预测概率y_pred）
    def forward(self, x):
        # 1. 线性层计算：x（输入）→ linear(x)（线性预测值，范围无约束）
        # 2. Sigmoid激活：将线性预测值映射到[0,1]，得到分类概率（接近0为负类，接近1为正类）
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

# 创建逻辑回归模型实例（此时已初始化权重w和偏置b）
model = LogisticRegressionModel()

# 定义损失函数：二元交叉熵损失（BCELoss），用于逻辑回归的分类任务
# reduction='sum'：对所有样本的损失值求和（而非求平均），便于后续梯度计算
criterion = torch.nn.BCELoss(reduction='sum')
# 定义优化器：随机梯度下降（SGD），用于更新模型参数（w和b）
# model.parameters()：自动获取模型中所有可训练参数（即linear层的w和b）
# lr=0.01：学习率（步长），控制每次参数更新的幅度
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 模型训练循环：迭代1000轮（epoch），逐步优化参数以降低损失
for epoch in range(10000):
    # 1. 前向传播：将输入x_data传入模型，得到所有样本的预测概率y_pred
    y_pred = model(x_data)
    # 2. 计算损失：对比预测概率y_pred和真实标签y_data，得到当前轮次的总损失
    loss = criterion(y_pred, y_data)
    # 打印当前轮次和损失值（loss.item()：将张量转为Python数值，避免打印冗余信息）
    print("epoch: {}, loss: {}".format(epoch, loss.item()))
    # 3. 清零梯度：优化器默认会累积梯度，需先清零，避免上一轮梯度干扰当前轮
    optimizer.zero_grad()
    # 4. 反向传播：自动计算损失对所有可训练参数（w和b）的梯度（链式法则）
    loss.backward()
    # 5. 参数更新：根据梯度和学习率，更新权重w和偏置b（w = w - lr*dw，b = b - lr*db）
    optimizer.step()

# --------------- 训练完成后：绘制逻辑回归的概率曲线 ---------------
# 生成x轴数据：用numpy的linspace生成0到10之间的200个等间隔点（模拟更多“学习时长”）
x = np.linspace(0, 10, 200)
# 将numpy数组x转为PyTorch张量，并调整形状为(200,1)（200个样本，每个样本1个特征）
# .float()：确保输入张量类型为float32，与模型参数（默认float32）一致，避免类型不匹配
x_t = torch.tensor(x).view((200, 1)).float()
# 用训练好的模型预测x_t的概率：得到200个点的预测概率（形状(200,1)）
y_t = model(x_t)
# 将预测概率张量y_t转为numpy数组，便于matplotlib绘图
y = y_t.data.numpy()

# 绘制概率曲线：x轴为“学习时长”，y轴为“及格概率”，曲线体现两者的非线性关系（Sigmoid形状）
plt.plot(x, y)
# 绘制参考线：y=0.5（概率阈值，通常以此划分正负类：>0.5预测为正类，<0.5预测为负类）
# c='r'：线条颜色为红色，便于区分
plt.plot([0,10],[0.5,0.5],c='r')
# 设置x轴标签：说明x轴含义（此处假设输入特征为“学习时长（Hours）”）
plt.xlabel('Hours')
# 设置y轴标签：说明y轴含义（“及格概率（Probability of Pass）”）
plt.ylabel('Probability of Pass')
# 显示网格线：便于观察曲线与阈值的交点（如“学习时长多少时及格概率达50%”）
plt.grid()
# 显示绘制的图像
plt.show()
